use std::{
    collections::HashMap,
    sync::{Arc, Mutex},
};

use model_graph_types::{container::workflow::WorkflowEngineRunOptions, modeling::Value};

#[derive(Debug)]
pub struct WorkflowContext {
    pub debug: bool,
    pub run_options: WorkflowEngineRunOptions,
    pub node_outputs: HashMap<String, HashMap<String, Value>>,
    pub variables: HashMap<String, Value>,
    pub result: Option<bool>,
}

impl WorkflowContext {
    /// 创建
    pub fn new(options: WorkflowEngineRunOptions) -> Self {
        let debug = options.debug.map_or(false, |v| v);
        Self {
            debug,
            run_options: options,
            node_outputs: HashMap::new(),
            variables: HashMap::new(),
            result: None,
        }
    }
}

impl WorkflowContext {
    pub fn get_variable_value(&self) {}
    pub fn set_variable_value(&self) {}
}

pub trait Getter {
    fn get(&self, key: &String) -> anyhow::Result<Value>;

    fn get_from_node(&self, key: &String, node_id: &String) -> anyhow::Result<Value>;

    fn get_map_values(&self, node_pre: Option<String>) -> anyhow::Result<HashMap<String, Value>>;
}

pub trait Setter {
    fn set(&self, key: &String, value: Value) -> anyhow::Result<()>;

    fn set_node_result(
        &self,
        node_id: &String,
        result: HashMap<String, Value>,
    ) -> anyhow::Result<()>;
}

impl Getter for Arc<Mutex<WorkflowContext>> {
    /// 获取值
    fn get(&self, key: &String) -> anyhow::Result<Value> {
        self.lock()
            .map_err(|err| anyhow::anyhow!("获取锁失败:{}", err))?
            .get(key)
    }
    /// 获取值
    fn get_from_node(&self, key: &String, node_id: &String) -> anyhow::Result<Value> {
        self.lock()
            .map_err(|err| anyhow::anyhow!("获取锁失败:{}", err))?
            .get_from_node(key, node_id)
    }

    fn get_map_values(&self, node_pre: Option<String>) -> anyhow::Result<HashMap<String, Value>> {
        self.lock()
            .map_err(|err| anyhow::anyhow!("获取锁失败:{}", err))?
            .get_map_values(node_pre)
    }
}

impl Setter for Arc<Mutex<WorkflowContext>> {
    /// 获取值
    fn set(&self, key: &String, value: Value) -> anyhow::Result<()> {
        self.lock()
            .map_err(|err| anyhow::anyhow!("获取锁失败:{}", err))?
            .set(key, value)
    }
    /// 获取值
    fn set_node_result(
        &self,
        node_id: &String,
        result: HashMap<String, Value>,
    ) -> anyhow::Result<()> {
        self.lock()
            .map_err(|err| anyhow::anyhow!("获取锁失败:{}", err))?
            .set_node_result(node_id, result)
    }
}

impl Getter for WorkflowContext {
    /// 获取值
    fn get(&self, key: &String) -> anyhow::Result<Value> {
        //获取变量值
        //1.从variables中获取
        if let Some(v) = self.variables.get(key) {
            return Ok(v.clone());
        }
        Ok(Value::None)
    }

    /// 获取值
    fn get_from_node(&self, key: &String, node_id: &String) -> anyhow::Result<Value> {
        //获取该节点
        if let Some(result) = self.node_outputs.get(node_id) {
            if let Some(v) = result.get(key) {
                return Ok(v.clone());
            }
        }
        Ok(Value::None)
    }

    /// 将context中所有的值转换为map
    fn get_map_values(&self, node_pre: Option<String>) -> anyhow::Result<HashMap<String, Value>> {
        let mut cache = HashMap::new();

        for (node_key, node_values) in &self.node_outputs {
            let mut node_cache = HashMap::new();
            for (key, value) in node_values {
                node_cache.insert(key.clone(), value.clone());
            }
            if let Some(v) = &node_pre {
                cache.insert(format!("{}{}", v, node_key), Value::Object(node_cache));
            } else {
                cache.insert(node_key.clone(), Value::Object(node_cache));
            }
        }
        Ok(cache)
    }
}

impl WorkflowContext {
    pub fn set(&mut self, key: &String, value: Value) -> anyhow::Result<()> {
        Ok(())
    }

    pub fn set_node_result(
        &mut self,
        node_id: &String,
        result: HashMap<String, Value>,
    ) -> anyhow::Result<()> {
        //
        tracing::debug!("设置结果[{}]:{:?}", node_id, result);
        self.node_outputs.insert(node_id.clone(), result);
        Ok(())
    }

    pub fn put_variables(&mut self, variables: HashMap<String, Value>) {
        for (k, v) in variables {
            self.variables.insert(k, v);
        }
    }
}
